package it.aqquadro.arex;

import java.io.IOException;
import java.io.InputStreamReader;
import java.util.logging.Logger;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;


public abstract class Arex {

	private static final Logger log = Logger.getLogger( Arex.class.getName() );
	
	protected static final String CACHE_CONTROL_HEADER = "Cache-Control";

	protected static final String DEFAULT_CHARACTER_ENCODING = "UTF-8";
	
	protected static final String DEFAULT_JSONP_CALLBACK = "callback";
	
	protected static final String JSON_CONTENT_TYPE = "application/json";

	protected static final String JSONP_CONTENT_TYPE = "application/javascript";
	
	protected static final String NO_CACHE_HEADER_VALUE = "no-store, no-cache, must-revalidate";

	protected final Gson gson = new Gson();
	
	protected HttpServletRequest req;
	
	protected HttpServletResponse resp;	

	/**
	 * 
	 * @param req
	 * @param resp
	 * @param us
	 */
	public Arex( HttpServletRequest req, HttpServletResponse resp ) {
		this.req = req;
		this.resp = resp;		
	}

	protected String getCharacterEncoding() { return DEFAULT_CHARACTER_ENCODING; }

	protected String getJSONPCallback() { return DEFAULT_JSONP_CALLBACK; }
	
	protected boolean isCached() { return false; }	
	
	/**
	 * REST methods
	 * @throws IOException
	 */
	public abstract void head() throws IOException;
	public abstract void options() throws IOException;
	public abstract void post() throws IOException;
	public abstract void put() throws IOException;	
	public abstract void trace() throws IOException;
	public abstract void get() throws IOException;
	public abstract void delete() throws IOException;
		
	/**
	 * Legge il body della richiesta, trasforma la stringa json in un oggetto
	 * @return oggetto json
	 */
	protected final JsonElement loadGSONRequestBody() {
		
		JsonParser parser = new JsonParser();
		
		return parser.parse( readRequestBody() );		
	}

	/**
	 * Apre lo stream sul body della richiesta
	 * @return lo stream
	 */
	protected final InputStreamReader readRequestBody() {
		ServletInputStream is = null;
		
		try {
			is = this.req.getInputStream();
		} catch (IOException e) {
			log.severe( "Errore durante la lettura del corpo della richiesta" );
			log.severe( e.getMessage() );
		}
		
		return new InputStreamReader(is); 
	}
	
	/**
	 * Legge il body della richiesta, trasforma la stringa in un tipo di oggetto scelto
	 * @param <T>
	 * @param classOf
	 * @return
	 */
	protected final <T> T loadGSONRequestBody( Class <T> classOf ) {
		return gson.fromJson( readRequestBody(), classOf );
	}
	
	/**
	 * Esegue il toString dell'elemento e scrive sulla response
	 * @param jsonElement
	 * @throws IOException
	 */
	protected final void writeGSON( JsonElement jsonElement ) throws IOException {
		this.writeJSON( jsonElement.toString() );
	}
	
	/**
	 * Scrive sulla response la stringa json passata
	 * @param jsonString
	 * @throws IOException
	 */
	protected final void writeJSON( String jsonString ) throws IOException {	
		
		String jsonpCallback = getJSONPCallback();
		
		String callback = (String) this.req.getParameter( jsonpCallback );
		
		if ( callback != null ) {
			/**
			 * JSONP
			 */
			
			String jsonP = callback + "( " + jsonString + " );";
			
			this.write( JSONP_CONTENT_TYPE, getCharacterEncoding(), jsonP );
			
		} else {
			/**
			 * classic JSON
			 */
			
			this.write( JSON_CONTENT_TYPE, getCharacterEncoding(), jsonString );
			
		}
	}
	
	/**
	 * Scrive sulla response la stringa data(vanno specificati anche parametri raw)
	 * @param contentType
	 * @param characterEncoding
	 * @param response
	 * @throws IOException
	 */
	protected final void write( String contentType, String characterEncoding, String response ) throws IOException {
		
		if( !isCached() ) this.resp.setHeader( CACHE_CONTROL_HEADER, NO_CACHE_HEADER_VALUE );
		
		this.resp.setCharacterEncoding( characterEncoding );
		
		this.resp.setContentType( contentType );
		
		log.info( response );
		
		this.resp.getWriter().print( response );
	}
}